﻿using Core.Database;
using Core.Info;
using Core.Utility;
using Core.Utility.Helper;
using Core.Log;
using FreeSql;
using FreeSql.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Core.Database.Model;
using Microsoft.Extensions.Logging;

namespace Core.Database
{
    /// <summary>
    /// 本地存储工厂（通常为Sqlite）
    /// </summary>
    public static class LocalDataStorageFactory
    {
        private static readonly object _lockObject = new object();
        private static IFreeSql? database;

        /// <summary>
        /// 配置文件缓存文件名
        /// </summary>
        public const string _cacheConfigFileName = "AppLocalStorage";

        /// <summary>
        /// 配置节名称
        /// </summary>
        public const string _sectionName = "DatabaseStorage";

        /// <summary>
        /// 静态构造函数
        /// </summary>
        static LocalDataStorageFactory()
        {
            Config = new LocalDataStorageConfig();
            //数据存储数据库配置Json字符串
            try
            {
                var dataStorageDbConfigJson = JsonCacheHelper.GetJsonCache(_cacheConfigFileName, _sectionName, "DbConfig");
                var config = JsonHelper.Deserialize<DatabaseConfig>(dataStorageDbConfigJson);
                if (config != null)
                {
                    Config = config?.Decrypt() ?? new LocalDataStorageConfig();
                }
                else
                {
                    Config = new LocalDataStorageConfig();
                }
            }
            catch (Exception ex)
            {
                AppTrace.Logger.LogError(ex, "加载本地数据存储配置失败");
            }
        }


        /// <summary>
        /// 加载数据库配置
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public static ReturnResultInfo LoadDatabaseConfig()
        {
            ReturnResultInfo res = new ReturnResultInfo();
            var config = new LocalDataStorageConfig();
            //数据存储数据库配置Json字符串
            try
            {
                var dataStorageDbConfigJson = JsonCacheHelper.GetJsonCache(_cacheConfigFileName, _sectionName, "DbConfig");
                config = JsonHelper.Deserialize<DatabaseConfig>(dataStorageDbConfigJson);
                if (config != null)
                {
                    var model = config.DeepCopyByJson();
                    config = model.Decrypt();
                }
                else
                {
                    config = new LocalDataStorageConfig();
                }
                if (Config != config)
                {
                    lock (_lockObject)
                    {
                        if (Config != config)
                        {
                            Config = config;
                        }
                    }
                }
                res.Successed = true;
                res.Message = "操作成功";
            }
            catch (Exception ex)
            {
                res.Message = $"本地数据存储-加载数据库配置，操作失败：\r\n{ex.DetailMessage()}";
                res.Info = $"本地数据存储-加载数据库配置，操作失败：\r\n{ex.DetailInfo()}";
            }
            return res;
        }

        /// <summary>
        /// 保存数据库配置
        /// </summary>
        /// <param name="config"></param>
        /// <returns></returns>
        public static ReturnResultInfo SaveDatabaseConfig(LocalDataStorageConfig config)
        {
            ReturnResultInfo res = new ReturnResultInfo();
            try
            {
                LocalDataStorageConfig model = config.DeepCopyByJson().Encrypt();
                DatabaseConfig databaseConfigEntity = model;
                //加密数据,并序列化为Json
                string? json = JsonHelper.Serialize(model);
                var saveRes = JsonCacheHelper.SetJsonCache(_cacheConfigFileName, _sectionName, "DbConfig", json);
                if (saveRes)
                {
                    if (!Config.Equals(config))
                    {
                        lock (_lockObject)
                        {
                            if (!Config.Equals(config))
                            {
                                Config = databaseConfigEntity.Decrypt();
                            }
                        }
                    }
                    res.Successed = true;
                    res.Message = "操作成功！";
                }
            }
            catch (Exception ex)
            {
                res.Message = $"本地数据存储-保存数据库配置，操作失败：\r\n{ex.DetailMessage()}";
                res.Info = $"本地数据存储-保存数据库配置，操作失败：\r\n{ex.DetailInfo()}";
            }
            return res;
        }

        /// <summary>
        /// 刷新数据库连接，通常在调用<see cref="SaveDatabaseConfig(LocalDataStorageConfig)"/> 后调用
        /// </summary>
        /// <param name="testConnectionTimeout">测试连接超时时间，小于等于0，代表不需要测试连接；单位：秒</param>
        /// <returns></returns>
        public static ReturnResultInfo RefreshDatabase(int testConnectionTimeout = 0)
        {
            ReturnResultInfo res = new ReturnResultInfo();
            try
            {
                if (Config == null)
                {
                    res.Message = $"本地数据存储-刷新数据库配置，操作失败：\r\n未获取有效配置；配置参数：{nameof(Config)} = NULL ";
                    return res;
                }
                var connection = DatabaseFactory.GenerateConnectionString(Config);
                DataType dataType = Converter(Config.DatabaseType);
                if (dataType == DataType.Custom)
                {
                    res.Message = $"本地数据存储-刷新数据库配置，操作失败：\r\n{Config.DatabaseType.ToString()}不是合法的数据库类型，无法实例化数据库连接";
                    return res;
                }
                //校验数据能否连接数据库
                if (testConnectionTimeout > 0)
                {
                    var connectionConfigRes = DatabaseFactory.TestConnection(Config, testConnectionTimeout);
                    if (connectionConfigRes.Successed == false)
                    {
                        res.Message = $"本地数据存储-刷新数据库连接，操作失败：\r\n{connectionConfigRes.Message}";
                        res.Info = $"本地数据存储-刷新数据库连接，操作失败：\r\n{connectionConfigRes.Info}";
                        return res;
                    }
                }
                var freeSqlBuilder = new FreeSqlBuilder()
                    .UseConnectionString(dataType, connection)
                    .UseMonitorCommand(cmd =>
                    {
                        if (RunTimeInfo.IsDebug || RunTimeInfo.IsDevelopment)
                        {
                            if (RunTimeInfo.IsDebug || RunTimeInfo.IsDevelopment)
                            {
                                StringBuilder sb = new StringBuilder();
                                sb.AppendLine("本地数据存储执行SQL：");
                                sb.AppendLine($"CommandText：{cmd.CommandText}");
                                sb.AppendLine($"CommandType：{cmd.CommandType}");
                                AppTrace.Logger.LogDebug(sb.ToString());
                            }
                        }
                    });
                var freeSql = freeSqlBuilder.Build();
                if (database != freeSql)
                {
                    lock (_lockObject)
                    {
                        if (database != freeSql)
                        {
                            Database = freeSql;
                            res.Message = "操作成功！";
                            res.Successed = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                res.Message = $"本地数据存储-刷新数据库连接，操作失败：\r\n{ex.DetailMessage()}";
                res.Info = $"本地数据存储-刷新数据库连接，操作失败：\r\n{ex.DetailInfo()}";
            }
            return res;
        }

        /// <summary>
        /// 默认本地数据存储
        /// </summary>
        /// <returns></returns>
        private static IFreeSql DefaultLocalDataStorage()
        {
            var freeSqlBuilder = new FreeSqlBuilder()
                .UseConnectionString(DataType.Sqlite, @$"Data Source=LocalStore\local_data_storage.db;Pooling=true;Max Pool Size=100")
                .UseMonitorCommand(cmd =>
                {
                    if (RunTimeInfo.IsDebug || RunTimeInfo.IsDevelopment)
                    {
                        if (RunTimeInfo.IsDebug || RunTimeInfo.IsDevelopment)
                        {
                            StringBuilder sb = new StringBuilder();
                            sb.AppendLine("默认本地数据存储执行SQL：");
                            sb.AppendLine($"CommandText：{cmd.CommandText}");
                            sb.AppendLine($"CommandType：{cmd.CommandType}");
                            AppTrace.Logger.LogDebug(sb.ToString());
                        }
                    }
                });
            var freeSql = freeSqlBuilder.Build();
            AppTrace.Logger.LogWarning("未能获取到正确的数据库存储信息，使用默认本地数据存储");
            return freeSql;
        }

        /// <summary>
        /// 类型转换
        /// </summary>
        /// <param name="databaseTypeEnum"></param>
        /// <returns></returns>
        public static DataType Converter(this DatabaseType? databaseTypeEnum)
        {
            DataType dataType = DataType.Custom;
            switch (databaseTypeEnum)
            {
                case DatabaseType.MySql:
                    dataType = DataType.MySql;
                    break;
                case DatabaseType.SqlServer:
                    dataType = DataType.SqlServer;
                    break;
                case DatabaseType.Sqlite:
                    dataType = DataType.Sqlite;
                    break;
                case DatabaseType.Oracle:
                    dataType = DataType.Oracle;
                    break;
                case DatabaseType.PostgreSQL:
                    dataType = DataType.PostgreSQL;
                    break;
                default:
                    break;
            }
            return dataType;
        }

        /// <summary>
        /// 配置
        /// </summary>
        public static LocalDataStorageConfig Config
        {
            private set;
            get;
        }

        /// <summary>
        /// 数据库
        /// </summary>
        public static IFreeSql Database
        {
            get
            {
                if (database == null)
                {
                    lock (_lockObject)
                    {
                        if (database == null)
                        {
                            var refreshRes = RefreshDatabase();
                            if (!refreshRes.Successed)
                            {
                                AppTrace.Logger.LogError($"本地数据存储-初始化加载数据库连接，操作失败：\r\n{refreshRes.Info}");
                            }
                        }
                    }
                }
                return database ?? DefaultLocalDataStorage();
            }
            private set => database = value;
        }

        /// <summary>
        /// 通过<see cref="TableAttribute"/>获取数据库的实体类型
        /// </summary>
        /// <returns></returns>
        public static Type[] GetTypesByTableAttribute()
        {
            //仅添加实体基类
            List<Type> res = new List<Type>();
            //加载一次根目录及子目录下的程序集
            //AssemblyHelper.LoadAssemblyByFileNameRegex(AppInfo.RootDirectory, "^(Entity|Plugin|Module).*.(dll|exe)$", true);

            List<Type> tableAssembies = new List<Type>();
            var assemblyExportedTypes = AppDomain.CurrentDomain.GetAssemblies()
                .Where(p => !p.IsDynamic)
                .SelectMany(a => a.ExportedTypes.Where(t => typeof(ILocalDataStorageEntity).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract))
                .ToArray();
            //Assembly.GetAssembly(typeof(ILocalDataStorageEntity))?.GetExportedTypes();
            if (assemblyExportedTypes != null)
            {
                foreach (Type type in assemblyExportedTypes)
                {
                    if (IsEnabledSyncStructure(type))
                    {
                        tableAssembies.Add(type);
                    }
                }

                foreach (var typeItem in tableAssembies)
                {
                    Type addType = typeItem;
                    if (typeItem.BaseType != null)
                    {
                        //判断基类是否为表结构同步类
                        if(typeItem.BaseType.GetInterfaces().Contains(typeof(ILocalDataStorageEntity)) && IsEnabledSyncStructure(typeItem.BaseType))
                        {
                            addType = typeItem.BaseType;
                        }              
                    }
                    if (tableAssembies.Contains(addType))
                    {
                        res.Add(addType);
                    }
                }
            }
            return res.ToArray();
        }

        /// <summary>
        /// 是否启用同步结构
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsEnabledSyncStructure(Type type)
        {
            bool res = false;
            foreach (Attribute attribute in type.GetCustomAttributes())
            {
                if (attribute is TableAttribute tableAttribute)
                {
                    if (tableAttribute.DisableSyncStructure == false)
                    {
                        res = true;
                        break;
                    }
                }
            }
            return res;
        }
    }
}
